package org.vincen.helloworld.javassist;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Test;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtField.Initializer;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;

public class PracticeJavassist2 {

	//使用javassist直接生成一个class文件
	@Test
	public void test1() throws CannotCompileException, NotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.makeClass("org.vincen.Point");
		StringBuilder constructorBody = new StringBuilder();
		StringBuilder methodBody = new StringBuilder();
		
		//设置一个属性
        //参数  1：属性类型  2：属性名称  3：所属类CtClass
        CtField field1 = new CtField(pool.get("java.lang.String"), "name", cc);
        field1.setModifiers(Modifier.PRIVATE);
        //设置name属性的get/set方法
        cc.addMethod(CtNewMethod.setter("setName", field1));
        cc.addMethod(CtNewMethod.getter("getName", field1));
        cc.addField(field1, Initializer.constant("default"));

        //设置一个构造方法
        //参数  1：参数类型   2：所属类CtClass
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, cc);   
        constructorBody.append("{\n name=\"me\";\n}");   
        constructor.setBody(constructorBody.toString());   
        cc.addConstructor(constructor);
        
        //设置一个方法
        //参数：  1：返回类型  2：方法名称  3：传入参数类型  4：所属类CtClass
        //此处也可以使用CtNewMethod这个类
        CtMethod method1 = new CtMethod(CtClass.voidType, "execute", new CtClass[]{}, cc);   
        method1.setModifiers(Modifier.PUBLIC);
        methodBody.append("{\n System.out.println(name);");   
        methodBody.append("\n System.out.println(\"execute ok\");");   
        methodBody.append("\n return ;");   
        methodBody.append("\n}");   
        method1.setBody(methodBody.toString());   
        cc.addMethod(method1);
        //使用如下的writeFile()可以将生成的类，直接写入磁盘上的class文件中。
//        cc.writeFile();
        Class<?> c= cc.toClass();   
        Object o = c.newInstance();   
        Method method=o.getClass().getMethod("execute", new Class[]{});   
        //调用字节码生成类的execute方法   
        method.invoke(o, new Object[]{});  
		
	}
	
	@Test
	public void test2() throws InstantiationException, IllegalAccessException{
		ProxyFactory factory = new ProxyFactory();
		//设置父类，ProxyFactory将会动态生成一个类，继承该父类
		factory.setSuperclass(JavassistClass.class);
		//设置过滤器，判断哪些方法调用需要被拦截
		factory.setFilter(new MethodFilter() {
			public boolean isHandled(Method arg0) {
				if(arg0.getName().equals("getName")){
					return true;
				}
				return false;
			}
		});
		//以下的setHandler方法已经不推荐使用
//		factory.setHandler(new MethodHandler() {
//			public Object invoke(Object arg0, Method arg1, Method arg2, Object[] arg3)
//					throws Throwable {
//				//拦截后前置处理，改写name属性的内容   
//                //实际情况可根据需求修改   
//                JavassistClass o=(JavassistClass) arg0;   
//                o.setName("haha");
//                return arg2.invoke(arg0, arg3);
//			}
//		});
//		Class<?> c=factory.createClass();   
//        JavassistClass object=(JavassistClass) c.newInstance();   
//        System.out.println(object.getName());
		
		Class<?> c = factory.createClass();
		JavassistClass jc = (JavassistClass)c.newInstance();
		//设置一个处理器对象
		((Proxy)jc).setHandler(new MethodHandler() {
			public Object invoke(Object arg0, Method arg1, Method arg2, Object[] arg3)
					throws Throwable {
				//拦截后前置处理，改写name属性的内容
                //实际情况可根据需求修改
				JavassistClass o = (JavassistClass)arg0;
				//注意，此处调用JavassistClass对象的方法，一定要先写过滤器，要不然，
				//这个对象调用了其自身， 会死循环。
				o.setName("vincen");
				System.out.println(o.getClass().getName());
				System.out.println("------abc------");
				return arg2.invoke(arg0, arg3);
			}
		});
		System.out.println(jc.getName());
	}
	
}

class JavassistClass{
	private String name = "default";
	public JavassistClass(){
		name = "me";
	}
    public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void execute(){
		//此处实际是使用的getName方法
		System.out.println(name);
		System.out.println("execute ok");
	}
	public void doIt(){
		System.out.println("just do it");
	}
}
